/*
 *
 * Copyright 2024 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <condition_variable>
#include <iostream>
#include <memory>
#include <mutex>
#include <string>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include <grpcpp/grpcpp.h>

#include "health.pb.h"
#include "helloworld.pb.h"


ABSL_FLAG(std::string, target, "localhost:50051", "Server address");

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using grpc::health::v1::HealthCheckRequest;
using grpc::health::v1::HealthCheckResponse;
using helloworld::HelloReply;
using helloworld::HelloRequest;


class GreeterClient {
public:
    GreeterClient(std::shared_ptr<Channel> channel)
            : stub_(Greeter::NewStub(channel)),
              health_stub_(Health::NewStub(channel)) {}

    // Assembles the client's payload, sends it and presents the response back
    // from the server.
    std::string SayHello(const std::string &user) {
        // Data we are sending to the server.
        HelloRequest request;
        request.set_name(user);

        // Container for the data we expect from the server.
        HelloReply reply;

        // Context for the client. It could be used to convey extra information to
        // the server and/or tweak certain RPC behaviors.
        ClientContext context;

        // The actual RPC.
        std::mutex mu;
        std::condition_variable cv;
        bool done = false;
        Status status;
        stub_->async()->SayHello(&context, &request, &reply,
                                 [&mu, &cv, &done, &status](Status s) {
                                     status = std::move(s);
                                     std::lock_guard<std::mutex> lock(mu);
                                     done = true;
                                     cv.notify_one();
                                 });

        std::unique_lock<std::mutex> lock(mu);
        while (!done) {
            cv.wait(lock);
        }

        // Act upon its status.
        if (status.ok()) {
            return reply.message();
        } else {
            std::cout << status.error_code() << ": " << status.error_message()
                      << std::endl;
            return "RPC failed";
        }
    }

    void CheckHealth(const std::string &message) {
        ClientContext context;
        HealthCheckResponse response;
        Status status = health_stub_->Check(
                &context, HealthCheckRequest::default_instance(), &response);
        if (!status.ok()) {
            std::cerr << "Failed to check service health: " << status.error_code()
                      << ": " << status.error_message() << "\n";
            return;
        }
        std::cout << message << ": " << response.DebugString();
    }

private:
    std::unique_ptr<Greeter::Stub> stub_;
    std::unique_ptr<Health::Stub> health_stub_;
};

int main(int argc, char **argv) {
    helloworld::s
    absl::ParseCommandLine(argc, argv);
    // Instantiate the client. It requires a channel, out of which the actual RPCs
    // are created. This channel models a connection to an endpoint specified by
    // the argument "--target=" which is the only expected argument.
    std::string target_str = absl::GetFlag(FLAGS_target);
    // We indicate that the channel isn't authenticated (use of
    // InsecureChannelCredentials()).
    grpc::ChannelArguments args;
    args.SetServiceConfigJSON(
            "{\"healthCheckConfig\": "
            "{\"serviceName\": \"\"}}");
    GreeterClient greeter(grpc::CreateCustomChannel(
            target_str, grpc::InsecureChannelCredentials(), args));
    std::string user = "world";
    greeter.CheckHealth("Before call");
    std::string reply = greeter.SayHello(user);
    std::cout << "Greeter received: " << reply << std::endl;
    greeter.CheckHealth("After call");
    reply = greeter.SayHello(user);
    std::cout << "Greeter received: " << reply << std::endl;
    greeter.CheckHealth("After second call");
    return 0;
}
